package main

import (
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"time"
)

type LogLevel int

const (
	LL_Verbose LogLevel = iota
	LL_Debug
	LL_Info
	LL_Warning
	LL_Error
	LL_Critical
)

type _pos struct {
	start int
	end   int
}

func (pos _pos) isSub(p _pos) bool {
	return pos.start <= p.start && p.end <= pos.end
}

type _item struct {
	_pos
	name  string
	info  string
	value string
}

type _context struct {
	w     io.Writer
	depth int
	items map[_pos]*_item
}

func newContext(w io.Writer) *_context {
	return &_context{w: w, items: make(map[_pos]*_item)}
}

func (ctx *_context) decode(buf []byte, a any) {
	defer ctx.output()
	ctx.log(LL_Verbose, "decode start. all length is %d", len(buf))
	dec := NewDecoder(ctx.addItem, buf, 0)
	defer func() {
		r := recover()
		if r == nil {
			return
		}
		pos := dec.Pos()
		info := ""
		switch e := r.(type) {
		case string:
			info = e
		case error:
			info = e.Error()
		default:
			info = fmt.Sprint(e)
		}
		ctx.log(LL_Error, "decode error where pos is %d.%d that %s", pos/8, pos%8, info)
	}()
	dec.Decode(a, nil)
	ctx.log(LL_Info, "decode success. decode length is %d", dec.Pos()/8)
}

func (ctx *_context) addItem(start, end int, name, info, value string) {
	if start >= end {
		return
	}
	item := &_item{_pos: _pos{start, end}, name: name, info: info, value: value}

	if old, ok := ctx.items[item._pos]; ok {
		if len(name) == 0 && len(old.name) != 0 {
			item.name = old.name
		}
		if len(info) == 0 && len(old.info) != 0 {
			item.info = old.info
		}
		if len(value) == 0 && len(old.value) != 0 {
			item.value = old.value
		}
	}

	ctx.items[item._pos] = item
}

func (ctx *_context) outputItem(item *_item) {
	pre := strings.Repeat("  ", ctx.depth)
	start := item.start / 8
	end := (item.end-1)/8 + 1
	_, _ = fmt.Fprintf(ctx.w, "%s%s:%s:%s [%d, %d)\n", pre, item.name, item.info, item.value, start, end)
}

func (ctx *_context) log(ll LogLevel, format string, a ...any) {
	level := ""
	switch ll {
	case LL_Verbose:
		level = "Verbose"
	case LL_Debug:
		level = "Debug"
	case LL_Info:
		level = "Info"
	case LL_Warning:
		level = "Warning"
	case LL_Error:
		level = "Error"
	case LL_Critical:
		level = "Critical"
	}
	_, _ = fmt.Fprintf(os.Stderr, level+": "+format+"\n", a...)
}

func (ctx *_context) output() {
	if len(ctx.items) <= 1 {
		return
	}

	ctx.log(LL_Debug, "output item number is %d", len(ctx.items)-1)
	list := make([]*_item, 0, len(ctx.items))
	for _, v := range ctx.items {
		list = append(list, v)
	}
	sort.Slice(list, func(i, j int) bool {
		if list[i].start != list[j].start {
			return list[i].start < list[j].start
		}
		return list[i].end > list[j].end
	})

	list = list[1:] // 丢掉最外层的item
	for len(list) > 0 {
		list = ctx.outputTree(list)
	}
}

func (ctx *_context) outputTree(list []*_item) []*_item {
	root := list[0]
	ctx.outputItem(root)
	if len(list) <= 1 {
		return nil
	} else if !root.isSub(list[1]._pos) {
		return list[1:]
	}

	ctx.depth++
	defer func() { ctx.depth-- }()

	list = list[1:]
	for len(list) > 0 {
		if !root.isSub(list[0]._pos) {
			break
		}
		list = ctx.outputTree(list)
	}
	return list
}

func decode(w io.Writer, buf []byte) {
	_, _ = fmt.Fprintln(w, "SupportFileFilter:", fileFilter)
	var st Root
	newContext(w).decode(buf, &st)
}

func main() {
	out, err := os.OpenFile("out.log", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 644)
	if err != nil {
		_, _ = fmt.Fprintln(os.Stderr, err)
		return
	}
	defer out.Close()

	buf, err := os.ReadFile(os.Args[1])
	if err != nil {
		fmt.Println(err)
		return
	}
	decode(out, buf)
}

func fieldString(fieldMap []string, v uint64, size int) string {
	str := ""
	for i := 0; i < size; i++ {
		mask := uint64(1 << i)
		if v&mask == 0 {
			continue
		}
		if i >= len(fieldMap) || len(fieldMap[i]) == 0 {
			continue
		}
		v &= ^mask
		str += "|" + fieldMap[i]
	}
	if v != 0 {
		str += fmt.Sprintf("|%#X", v)
	}
	if len(str) <= 0 {
		return "0"
	}
	return str[1:]
}

type TimeStamp uint32

func (t TimeStamp) String() string {
	if t == 0 {
		return "invalid"
	}
	return time.Unix(int64(t), 0).String()
}

type WString string

func (s *WString) Unmarshal(dec *Decoder, info *ValueInfo) {
	pos := dec.Pos()

	bs := dec.ReadBytes(0)
	var cs []rune
	for i := 0; i < len(bs)-1; i += 2 {
		c := rune(0)
		if info.isBig {
			c = (rune(bs[i]) << 8) + rune(bs[i+1])
		} else {
			c = rune(bs[i]) + (rune(bs[i+1]) << 8)
		}
		if c == 0 {
			break
		}
		cs = append(cs, c)
	}
	*s = WString(cs)

	pos += len(cs) * 16
	if len(cs)*2 < len(bs)-1 {
		pos += 16
	}
	dec.Seek(pos)
}
